package com.petmarket.client;

import com.petmarket.common.AccountManger;
import com.petmarket.common.CommonClient;
import com.petmarket.common.PageResult;
import com.petmarket.common.TransactionManager;
import com.petmarket.contract.UserContract;
import com.petmarket.entity.User;
import com.petmarket.uitls.SpringUtils;
import org.fisco.bcos.sdk.jni.common.JniException;
import org.fisco.bcos.sdk.v3.BcosSDK;
import org.fisco.bcos.sdk.v3.client.Client;
import org.fisco.bcos.sdk.v3.codec.ContractCodec;
import org.fisco.bcos.sdk.v3.codec.ContractCodecException;
import org.fisco.bcos.sdk.v3.codec.datatypes.generated.tuples.generated.Tuple4;
import org.fisco.bcos.sdk.v3.crypto.signature.SignatureResult;
import org.fisco.bcos.sdk.v3.model.TransactionReceipt;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.ProxySignTransactionManager;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.dto.AbiEncodedRequest;
import org.fisco.bcos.sdk.v3.transaction.manager.transactionv1.utils.TransactionRequestBuilder;
import org.fisco.bcos.sdk.v3.transaction.model.exception.ContractException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;


@Service
public class UserClient extends CommonClient implements ApplicationRunner {
    public static final Logger logger = LoggerFactory.getLogger(UserClient.class.getName());

    /**
     * 开户
     * @param user
     * @throws ContractException
     */
    public void insert(User user) throws ContractException {
        UserContract userContract = (UserContract) getContractMap().get("User");
        //BigInteger count = userContract.count(BigInteger.valueOf(0),BigInteger.valueOf(200)).add(BigInteger.valueOf(1));
        TransactionReceipt receipt = userContract.insert(user.getId(),user.getName(),user.getBalance(),user.getAccount(),"0");
        logger.info("调用UserClient的insert方法");
        logger.info("结果：{}", receipt);
    }

    /**
     * 根据id查询用户数据
     * @param
     * @return
     * @throws ContractException
     */

    public User query(User user) throws Exception {
        UserContract userContract = (UserContract) getContractMap().get("User");
        Tuple4<String, String, String, String> getValue = userContract.select(user.getId());
        System.out.println(getValue);
        User resUser = new User();
        resUser.setId(user.getId());
        resUser.setName(getValue.getValue1());
        resUser.setBalance(getValue.getValue2());
        resUser.setAccount(getValue.getValue3());
        resUser.setIsDeleted(getValue.getValue4());

        if(resUser.getIsDeleted().equals("1") || getValue.getValue1().isEmpty()){
            throw new RuntimeException("执行失败");
        }
        logger.info("调用UserClient的query方法");
        logger.info("结果：{}", resUser);
        return resUser;
    }

    public User queryInner(BigInteger id) throws Exception {
        UserContract userContract = (UserContract) getContractMap().get("User");
        Tuple4<String, String, String,String> getValue = userContract.select(id);
        User user = new User();
        user.setId(id);
        user.setName(getValue.getValue1());
        user.setBalance(getValue.getValue2());
        user.setAccount(getValue.getValue3());
        user.setIsDeleted(getValue.getValue4());
        return user;
    }

    public void edit(User user) throws ContractException {
        UserContract userContract = (UserContract) getContractMap().get("User");

//        Tuple4<String, String, String, String> getValue = userContract.select(user.getId());
//        if(getValue.getValue2().equals("NA")){
//            logger.info("账号未开户");
//            return;
//        }
        logger.info("调用UserClient的edit方法");
        TransactionReceipt receipt = userContract.update(user.getId(),user.getName(),user.getBalance(),user.getAccount());
        logger.info("结果：{}", receipt.getMessage());
    }

    public void remove(User user) throws Exception {
        UserContract userContract = (UserContract) getContractMap().get("User");
        this.query(user);
        TransactionReceipt receipt = userContract.remove(user.getId());
        logger.info("调用UserClient的remove方法");
        logger.info("结果：{}", receipt);
    }

    public PageResult<User> getUserPage(Integer pageNo, Integer pageSize) throws Exception {
        PageResult<User> pageResult = new PageResult<>();
        UserContract userContract = (UserContract) getContractMap().get("User");
        BigInteger start = BigInteger.valueOf((pageNo - 1) * pageSize);
        BigInteger end = BigInteger.valueOf(pageNo * pageSize);
        System.out.println("start:" + start + "end:" + end);
        List<User> userList = new ArrayList<>();
        int count = 0,begin = start.intValue() + 1,last = end.intValue();
        while(count < pageSize) {
            User user = this.queryInner(BigInteger.valueOf(begin));
            if(user.getIsDeleted().equals("1")){
                continue;
            }
            else if(user.getIsDeleted().equals("")){
                break;
            }
            userList.add(user);
            count++;
            begin++;
        }
        pageResult.setRecords(userList);
        return pageResult;
    }

    public void transfer(BigInteger fromId, BigInteger toId, BigInteger amount,String hexPrivateKey) throws ContractCodecException, ContractException, JniException {

        // 为群组初始化client
        Client client = TransactionManager.getClient();

// 初始化ProxySignTransactionManager
        ProxySignTransactionManager proxySignTransactionManager;
// ProxySignTransactionManager可以接受AsyncTransactionSignercInterface的实现作为构造函数参数
        proxySignTransactionManager = new ProxySignTransactionManager(client, (hash, transactionSignCallback) -> {
            SignatureResult sign = client.getCryptoSuite().sign(hash, AccountManger.loadAccount(hexPrivateKey));
            transactionSignCallback.handleSignedTransaction(sign);
        });

        List<Object>params=new ArrayList<>();
        params.add(fromId);
        params.add(toId);
        params.add(amount);

        ContractCodec contractCodec=new ContractCodec(client.getCryptoSuite(),false);

// 对合约参数进行编解码
        byte[] abiEncoded = contractCodec.encodeMethod(UserContract.getABI(), "transfer", params);

// 采用链式构造AbiEncodedRequest，传入contractAddress、nonce、blockLimit等重要参数，最后使用buildAbiEncodedRequest结束构造。
        AbiEncodedRequest request =
                new TransactionRequestBuilder()
                        .setTo(CommonClient.getContractAddress("User"))
                        .setAbi(UserContract.getABI())
                        .setBlockLimit(client.getBlockLimit())
                        .setExtension("Hello".getBytes())
                        .setGasPrice(BigInteger.TEN)
                        .buildAbiEncodedRequest(abiEncoded);

// 同步发送上链，获得回执
        TransactionReceipt receipt = proxySignTransactionManager.sendTransaction(request);

        logger.info("调用petClient的edit(重载)方法");
        logger.info("结果：{}", receipt);

    }



    @Override
    public void run(ApplicationArguments args) throws Exception {
        BcosSDK sdk = SpringUtils.getBean("bcosSDK");
        deploy("User", UserContract.class, sdk);
    }

}
